home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 18 / CU Amiga Magazine's Super CD-ROM 18 (1997)(EMAP Images)(GB)[!][issue 1998-01].iso / CUCD / Online / hsc / source / hsclib / parse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-11-02  |  35.9 KB  |  1,329 lines

  1. /*
  2.  * This source code is part of hsc, a html-preprocessor,
  3.  * Copyright (C) 1995-1997  Thomas Aglassinger
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  */
  20. /*
  21.  * hsclib/parse.c
  22.  *
  23.  * parse file: handle for entities & tags
  24.  *
  25.  * updated:  4-Oct-1997
  26.  * created:  1-Jul-1995
  27.  *
  28.  */
  29.  
  30. #define NOEXTERN_HSCLIB_PARSE_H
  31.  
  32. #include "hsclib/inc_base.h"
  33.  
  34. #include "hsclib/defattr.h"
  35. #include "hsclib/deftag.h"
  36. #include "hsclib/idref.h"
  37. #include "hsclib/include.h"
  38. #include "hsclib/input.h"
  39. #include "hsclib/parse.h"
  40. #include "hsclib/posteval.h"
  41. #include "hscprj/project.h"
  42. #include "hsclib/skip.h"
  43. #include "hsclib/size.h"
  44. #include "hsclib/uri.h"
  45.  
  46. /*
  47.  *---------------------------
  48.  * misc. functions
  49.  *---------------------------
  50.  */
  51.  
  52. /*
  53.  * message_rplc
  54.  *
  55.  * message that tells user that a special char
  56.  * was replaced by its entity
  57.  */
  58. static VOID message_rplc(HSCPRC * hp, STRPTR what, STRPTR by)
  59. {
  60.     hsc_message(hp, MSG_RPLC_ENT,
  61.                 "replaced %q by %q", what, by);
  62. }
  63.  
  64. /*
  65.  * check_mbinaw
  66.  *
  67.  * check if tag occures in allowed context with other tags
  68.  */
  69. static BOOL check_mbinaw(HSCPRC * hp, HSCTAG * tag)
  70. {
  71.     BOOL ok = TRUE;
  72.  
  73.     /* check for tags that must be called before */
  74.     if (tag->mbi)
  75.     {
  76.         DLNODE *nd = hp->container_stack->first;
  77.         LONG found = 0;
  78.  
  79.         while (nd && !found)
  80.         {
  81.             HSCTAG *ctag = (HSCTAG *) nd->data;
  82.  
  83.             found = strenum(ctag->name, tag->mbi, '|', STEN_NOCASE);
  84.             nd = nd->next;
  85.  
  86.         }
  87.  
  88.         if (!found)
  89.         {
  90.             hsc_message(hp, MSG_MBI,
  91.                         "%T must be inside %t", tag, tag->mbi);
  92.             ok = FALSE;
  93.         }
  94.     }
  95.  
  96.     /* check for tags that are not to be called before */
  97.     if (tag->naw)
  98.     {
  99.         DLNODE *nd = hp->container_stack->last;
  100.         LONG found = 0;
  101.  
  102.         while (nd)
  103.         {
  104.             HSCTAG *ctag = (HSCTAG *) nd->data;
  105.  
  106.             found = strenum(ctag->name, tag->naw, '|', STEN_NOCASE);
  107.             if (found)
  108.             {
  109.                 hsc_message(hp, MSG_NAW,
  110.                             "%T not allowed within %T", tag, ctag);
  111.  
  112.                 ok = FALSE;
  113.             }
  114.             nd = dln_prev(nd);
  115.         }
  116.     }
  117.     return ok;
  118. }
  119.  
  120. /* enable output for a process */
  121. static void hp_enable_output(HSCPRC * hp, STRPTR cause)
  122. {
  123.     if (hp->suppress_output)
  124.     {
  125.         clr_estr(hp->whtspc);
  126.         D(fprintf(stderr, DHL "output enabled (%s)\n", cause));
  127.     }
  128.     hp->suppress_output = FALSE;
  129. }
  130.  
  131. /*
  132.  *---------------------------
  133.  * remove/append end tag
  134.  * from/to container_stack
  135.  *---------------------------
  136.  */
  137.  
  138. /*
  139.  * find_end_tag_node
  140.  *
  141.  * return first node of an end tag on the container stack;
  142.  * if not found, return NULL
  143.  */
  144. DLNODE *find_end_tag_node(HSCPRC * hp, STRPTR tagname)
  145. {
  146.     DLNODE *nd = find_dlnode_bw(hp->container_stack->last,
  147.                                 (APTR) tagname, cmp_strtag);
  148.     return nd;
  149. }
  150.  
  151. /*
  152.  * find_end_tag
  153.  *
  154.  * return first end tag on the container stack;
  155.  * if not found, return NULL
  156.  */
  157. HSCTAG *find_end_tag(HSCPRC * hp, STRPTR tagname)
  158. {
  159.     HSCTAG *tag = NULL;
  160.     DLNODE *nd = find_dlnode_bw(hp->container_stack->last,
  161.                                 (APTR) tagname, cmp_strtag);
  162.  
  163.     if (nd)
  164.     {
  165.         tag = (HSCTAG *) dln_data(nd);
  166.     }
  167.  
  168.     return tag;
  169. }
  170.  
  171. /*
  172.  * find_end_container
  173.  *
  174.  * search container stack for the first macro which is a
  175.  * container macro anjd return the cerresponding tag structure
  176.  */
  177. HSCTAG *find_end_container_macro(HSCPRC * hp)
  178. {
  179.     HSCTAG *tag = NULL;
  180.     DLNODE *nd = dll_last(hp->container_stack);
  181.  
  182.     while (nd)
  183.     {
  184.         HSCTAG *nd_tag = (HSCTAG *) dln_data(nd);
  185.  
  186.         if (nd_tag->option & HT_CONTENT)
  187.         {
  188.             tag = nd_tag;
  189.             nd = NULL;
  190.         }
  191.         else
  192.         {
  193.             nd = dln_prev(nd);
  194.         }
  195.     }
  196.  
  197.     return tag;
  198. }
  199.  
  200. /*
  201.  * append_end_tag
  202.  *
  203.  * create end tag and append it to tag-list;
  204.  * also clone options & attribute list of parent
  205.  * tag, if tag is a macro and has a closing tag.
  206.  *
  207.  * params: hp.....hscprc with container_stack to be modified
  208.  *         tagid..name of the new tag (eg "IMG")
  209.  * result: ptr to the new tag or NULL if no mem
  210.  */
  211. HSCTAG *append_end_tag(HSCPRC * hp, HSCTAG * tag)
  212. {
  213.     HSCTAG *end_tag;
  214.     DLLIST *taglist = hp->container_stack;
  215.  
  216.     end_tag = new_hsctag(tag->name);
  217.     if (end_tag)
  218.     {
  219.         BOOL ok = TRUE;
  220.         DLNODE *nd = NULL;
  221.  
  222.         /* copy important data of tag */
  223.         end_tag->option = tag->option;
  224.  
  225.         /* clone attributes, if tag is a
  226.          * macro tag and has a closing tag
  227.          */
  228.         if ((tag->option & HT_MACRO)
  229.             && (tag->option & HT_CLOSE))
  230.         {
  231.             ok = copy_local_varlist(end_tag->attr, tag->attr, MCI_APPCTAG);
  232.         }
  233.  
  234.         /* remeber position where start tag has been called */
  235.         /* (for message "end tag missing) */
  236.         end_tag->start_fpos = new_infilepos(hp->inpf);
  237.  
  238. #if 0                           /* TODO: remove this */
  239.         /* for container macros, remember position where content starts */
  240.         end_tag->end_fpos = clone_infilepos(tag->end_fpos);
  241. #endif
  242.  
  243.         /* insert tag in list */
  244.         if (ok)
  245.         {
  246.             nd = app_dlnode(taglist, end_tag);
  247.             if (!nd)
  248.             {
  249.                 del_hsctag((APTR) end_tag);
  250.                 end_tag = NULL;
  251.             }
  252.         }
  253.     }
  254.     return (end_tag);
  255. }
  256.  
  257. /*
  258.  * remove_end_tag
  259.  *
  260.  * remove tag from container stack, check for legal nesting,
  261.  * show up message if necessary.
  262.  *
  263.  * params: hp....hsc process
  264.  *         tag...tag to be removed
  265.  */
  266. VOID remove_end_tag(HSCPRC * hp, HSCTAG * tag)
  267. {
  268.     /* search for tag on stack of occured tags */
  269.     DLNODE *nd = find_dlnode_bw(hp->container_stack->last, (APTR) tag->name, cmp_strtag);
  270.     if (nd == NULL)
  271.     {
  272.         /* closing tag not found on stack */
  273.         /* ->unmatched closing tag without previous opening tag */
  274.         hsc_message(hp, MSG_UNMA_CTAG, "unmatched %C", tag);
  275.     }
  276.     else
  277.     {
  278.         /* closing tag found on stack */
  279.         HSCTAG *end_tag = (HSCTAG *) dln_data(nd);
  280.         STRPTR foundnm = (STRPTR) end_tag->name;
  281.         STRPTR lastnm = (STRPTR) dln_data(dll_last(hp->container_stack));
  282.  
  283.         /* check if name of closing tag is -not- equal
  284.          * to the name of the last tag last on stack
  285.          * ->illegal tag nesting
  286.          */
  287.         if (upstrcmp(lastnm, foundnm)
  288.             && !(tag->option | HT_MACRO)
  289.             && !(is_hsc_tag(tag)))
  290.         {
  291.             hsc_message(hp, MSG_CTAG_NESTING,
  292.                         "illegal end tag nesting (expected %c, found %C)",
  293.                         lastnm, tag);
  294.         }
  295.  
  296.         /* if closing tag has any attributes defined,
  297.          * it must be a closing macto tag. so copy
  298.          * the attributes of the closing tag to the
  299.          * attributes of the macro tag. therefor,
  300.          * the closing macro tag inherits the
  301.          * attributes of his opening macro
  302.          */
  303.         if (end_tag->attr)
  304.         {
  305.             set_local_varlist(tag->attr, end_tag->attr, MCI_APPCTAG);
  306.         }
  307.  
  308.         /* remove node for closing tag from container_stack */
  309.         del_dlnode(hp->container_stack, nd);
  310.     }
  311. }
  312.  
  313. /*
  314.  *---------------------------
  315.  * parse tag functions
  316.  *---------------------------
  317.  */
  318.  
  319. /*
  320.  * hsc_parse_tag
  321.  *
  322.  * parse tag (after "<")
  323.  */
  324. BOOL hsc_parse_tag(HSCPRC * hp)
  325. {
  326.     INFILE *inpf = hp->inpf;
  327.     STRPTR nxtwd = NULL;
  328.     DLNODE *nd = NULL;
  329.     HSCTAG *tag = NULL;
  330.     HSCTAG *now_tag_strip_whtspc = NULL;
  331.     ULONG tci = 0;              /* tag_call_id returned by set_tag_args() */
  332.     BOOL(*hnd) (HSCPRC * hp, HSCTAG * tag) = NULL;
  333.     BOOL open_tag;
  334.     DLLIST *taglist = hp->deftag;
  335.     BOOL rplc_lt = FALSE;       /* TRUE, if replace spc. char "<" */
  336.     BOOL hnd_result = TRUE;     /* result returned by handle */
  337.     BOOL unknown_tag = FALSE;   /* TRUE, if tag has not been defined before */
  338.     BOOL preceeding_whtspc = estrlen(hp->whtspc);
  339.  
  340.     /* init strings used inside tag-handles */
  341.     set_estr(hp->tag_name_str, infgetcw(inpf));
  342.     clr_estr(hp->tag_attr_str);
  343.     clr_estr(hp->tag_close_str);
  344.  
  345.     if (hp->smart_ent && preceeding_whtspc)
  346.     {
  347.         /*
  348.          * check for special char "<"
  349.          */
  350.         int ch = infgetc(inpf);
  351.  
  352.         /* check if next char is a white space */
  353.         if (hsc_whtspc(ch))
  354.         {
  355.             rplc_lt = TRUE;
  356.  
  357.             /* write "<" and white spaces */
  358.             message_rplc(hp, "<", "<");
  359.             hsc_output_text(hp, "", "<");
  360.         }
  361.         inungetc(ch, inpf);
  362.     }
  363.  
  364.     if (!rplc_lt)
  365.     {
  366.         /* get tag id */
  367.         nxtwd = infget_tagid(hp);
  368.  
  369.         if (!hp->fatal)
  370.         {
  371.             /* append tag-name to tag_name_str */
  372.             if (!hp->compact)
  373.             {
  374.                 app_estr(hp->tag_name_str, infgetcws(inpf));
  375.             }
  376.             app_estr(hp->tag_name_str, infgetcw(inpf));
  377.  
  378.             if (!hp->suppress_output)
  379.             {
  380.                 /* output tag currently processing
  381.                  * NOTE: the first tag of the source is skipped because
  382.                  *   hp->supress_ouptut is disable later */
  383.                 D(fprintf(stderr, DHL "tag <"));
  384.             }
  385.         }
  386.     }
  387.  
  388.     if (!hp->fatal && !rplc_lt)
  389.     {
  390.         BOOL write_tag = FALSE; /* flag: write tag text & attrs to output? */
  391.  
  392.         if (strcmp("/", nxtwd)) /* is it a closing tag? */
  393.         {
  394.             /*
  395.              *
  396.              * process start-tag
  397.              *
  398.              */
  399.             open_tag = TRUE;
  400.             if (!hp->suppress_output)
  401.             {
  402.                 D(fprintf(stderr, "%s>\n", nxtwd));
  403.             }
  404.             /* search for tag in list */
  405.             nd = find_dlnode(taglist->first, (APTR) nxtwd, cmp_strtag);
  406.             if (nd == NULL)
  407.             {
  408.                 hsc_message(hp, MSG_UNKN_TAG,   /* tag not found */
  409.                             "unknown %t", nxtwd);
  410.                 tag = new_hsctag(nxtwd);
  411.                 tag->option |= HT_UNKNOWN;
  412.                 unknown_tag = TRUE;
  413.             }
  414.             else
  415.             {
  416.                 tag = (HSCTAG *) nd->data;
  417.             }
  418.  
  419.             /* set handle-function */
  420.             hnd = tag->o_handle;
  421.  
  422.             /*
  423.              * handle options
  424.              */
  425.  
  426.             /* check for obsolete tag */
  427.             if (tag->option & HT_OBSOLETE)
  428.             {
  429.                 hsc_message(hp, MSG_TAG_OBSOLETE,
  430.                             "%T is obsolete", tag);
  431.             }
  432.  
  433.             /* check for jerk-tag */
  434.             if (tag->option & HT_JERK)
  435.             {
  436.                 hsc_message(hp, MSG_TAG_JERK,
  437.                             "%T is only used by %j", tag);
  438.             }
  439.  
  440.             /* only-once-tag occured twice? */
  441.             if ((tag->option & HT_ONLYONCE) && (tag->occured))
  442.             {
  443.                 hsc_message(hp, MSG_TAG_TOO_OFTEN,
  444.                             "%T occured too often", tag);
  445.             }
  446.  
  447.             /* set occured-flag */
  448.             if (tag->option & (HT_ONLYONCE | HT_REQUIRED))
  449.             {
  450.                 tag->occured = TRUE;
  451.             }
  452.  
  453.             /* check for "must be inside"/"not allowed within"-tags */
  454.             if (!check_mbinaw(hp, tag))
  455.             {
  456.                 hnd = NULL;
  457.             }
  458.  
  459.             /* clear (reset to default) attribute values of tag */
  460.             clr_varlist(tag->attr);
  461.  
  462.             /* set attributes or check for ">" */
  463.             if (!(tag->option & HT_SPECIAL))
  464.             {
  465.                 tci = set_tag_args(hp, tag);
  466.                 if (tci == MCI_ERROR)
  467.                 {
  468.                     skip_until_eot(hp, NULL);
  469.                     hnd = NULL;
  470.                 }
  471.  
  472.                 if (!hp->fatal)
  473.                 {
  474.                     /* set ">" in string that contains closing text */
  475.                     if (!hp->compact)
  476.                     {
  477.                         set_estr(hp->tag_close_str, infgetcws(inpf));
  478.                     }
  479.                     else
  480.                     {
  481.                         clr_estr(hp->tag_close_str);
  482.                     }
  483.                     app_estr(hp->tag_close_str, infgetcw(inpf));
  484.  
  485.                     /* check for succeeding white-space */
  486.                     if ((tag->option & HT_WHTSPC) && !infeof(inpf))
  487.                     {
  488.                         int ch = infgetc(inpf);
  489.  
  490.                         if (hsc_whtspc(ch))
  491.                         {
  492.                             if (hp->strip_badws)
  493.                             {
  494.                                 hp->strip_next2_whtspc = TRUE;
  495.                             }
  496.                             else if (!hp->strip_next2_whtspc)
  497.                             {
  498. #if 1
  499.                                 now_tag_strip_whtspc = tag;
  500. #else /* TODO: remove this */
  501.                                 hsc_message(hp, MSG_SUCC_WHTSPC,
  502.                                             "succeeding white-space for %T",
  503.                                             tag);
  504. #endif
  505.                             }
  506.                         }
  507.                         inungetc(ch, inpf);
  508.                     }
  509.                 }
  510.             }
  511.  
  512.             /* for AUTOCLOSE-tags, remove eventually existing
  513.              * end tags on stack */
  514.             if (tag->option & HT_AUTOCLOSE)
  515.             {
  516.                 DLNODE *nd = find_end_tag_node(hp, tag->name);
  517.  
  518.                 if (nd)
  519.                 {
  520.                     if (nd == dll_last(hp->container_stack))
  521.                     {
  522.                         D(fprintf(stderr, DHL "  autoclose </%s> before\n", tag->name));
  523.                         remove_end_tag(hp, tag);
  524.                     }
  525.                     else
  526.                     {
  527.                         HSCTAG *end_tag = (HSCTAG *) dln_data(dll_last(hp->container_stack));
  528.  
  529.                         D(fprintf(stderr,
  530.                                   DHL "  no autoclose because of <%s> \n",
  531.                                   end_tag->name));
  532.                     }
  533.                 }
  534.                 else
  535.                 {
  536.                     D(fprintf(stderr, DHL "  no autoclose neccessary\n"));
  537.                 }
  538.             }
  539.  
  540.             /* end-tag required? */
  541.             if ((tag->option & HT_CLOSE)
  542.                 || (tag->option & HT_AUTOCLOSE))
  543.             {
  544.                 /* yes: push current tag to container stack;
  545.                  * (current values of attributes will be
  546.                  * remembered */
  547.                 append_end_tag(hp, tag);
  548.             }
  549.         }
  550.         else
  551.         {
  552.             /*
  553.              *
  554.              * process end-tag
  555.              *
  556.              */
  557.  
  558.             /* get tag id */
  559.             nxtwd = infget_tagid(hp);   /* get tag id */
  560.             open_tag = FALSE;
  561.  
  562.             /* append tag-name to tag_name_str */
  563.             if (!hp->compact)
  564.             {
  565.                 app_estr(hp->tag_name_str, infgetcws(inpf));
  566.             }
  567.             app_estr(hp->tag_name_str, infgetcw(inpf));
  568.  
  569.             if (!hp->suppress_output)
  570.             {
  571.                 D(fprintf(stderr, "/%s>\n", nxtwd));
  572.             }
  573.  
  574.             /* search for tag in taglist */
  575.             /* (see if it exists at all) */
  576.             nd = find_dlnode(taglist->first, (APTR) nxtwd, cmp_strtag);
  577.             if (nd == NULL)
  578.             {
  579.                 /* closing tag is absolutely unknown */
  580.                 hsc_message(hp, MSG_UNKN_TAG,   /* tag not found */
  581.                             "unknown %c", nxtwd);
  582.                 skip_until_eot(hp, hp->tag_attr_str);
  583.             }
  584.             else
  585.             {
  586.                 tag = (HSCTAG *) nd->data;      /* fitting tag in taglist */
  587.  
  588.                 /* check for preceding white-spaces */
  589.                 if ((tag->option & HT_WHTSPC) && anyWhtspc(hp))
  590.                 {
  591.                     if (hp->strip_badws)
  592.                     {
  593.                         hp->strip_next_whtspc = TRUE;
  594.                     }
  595.                     else if (!hp->strip_next_whtspc)
  596.                     {
  597.                         hsc_message(hp, MSG_PREC_WHTSPC,
  598.                                     "preceding white space for %C", tag);
  599.                     }
  600.                 }
  601.  
  602.                 if (tag->option & (HT_CLOSE | HT_AUTOCLOSE))
  603.                 {
  604.                     /* set closing handle */
  605.                     hnd = tag->c_handle;
  606.  
  607.                     /* check for no args */
  608.                     if (!parse_wd(hp, ">"))
  609.                     {
  610.                         hsc_message(hp, MSG_CL_TAG_ARG,
  611.                                     "no attributes allowed for end-tags");
  612.                     }
  613.                     else
  614.                     {
  615.                         /* set ">" in string that contains closing text */
  616.                         if (!hp->compact)
  617.                         {
  618.                             set_estr(hp->tag_close_str, infgetcws(inpf));
  619.                         }
  620.                         app_estr(hp->tag_close_str, infgetcw(inpf));
  621.                     }
  622.  
  623.                     /* set values of attributes stored
  624.                      * in end-tag,
  625.                      * remove end-tag from stack
  626.                      */
  627.                     remove_end_tag(hp, tag);
  628.                 }
  629.                 else
  630.                 {
  631.                     /* illegal closing tag */
  632.                     hsc_message(hp, MSG_ILLG_CTAG,      /* tag not found */
  633.                                 "illegal %c", nxtwd);
  634.                     parse_gt(hp);
  635.                     tag = NULL;
  636.                 }
  637.             }
  638.         }
  639.  
  640.         /*
  641.          * processed for opening AND closing tag
  642.          */
  643.         write_tag = (!(tag) || !(tag->option & HT_NOCOPY));
  644.  
  645.         if (tag)
  646.         {
  647.             /*
  648.              * check if tag should be stripped
  649.              */
  650.             if (!postprocess_tagattr(hp, tag, open_tag))
  651.             {
  652.                 /* stripped tag with external reference */
  653.                 if (open_tag)
  654.                 {
  655.                     hsc_msg_stripped_tag(hp, tag, "external reference");
  656.                 }
  657.                 hnd = NULL;     /* don't call handle */
  658.                 write_tag = FALSE;      /* don't output tag */
  659.             }
  660.             else if (hp->strip_tags
  661.                      && strenum(tag->name, hp->strip_tags, '|', STEN_NOCASE))
  662.             {
  663.                 /* strip tag requested by user */
  664.                 if (!(tag->option & HT_SPECIAL))
  665.                 {
  666.                     if (open_tag)
  667.                     {
  668.                         hsc_msg_stripped_tag(hp, tag, "as requested");
  669.                     }
  670.                     hnd = NULL; /* don't call handle */
  671.                     write_tag = FALSE;  /* don't output tag */
  672.                 }
  673.                 else
  674.                 {
  675.                     hsc_message(hp, MSG_TAG_CANT_STRIP,
  676.                                 "can not strip special tag %T", tag);
  677.                 }
  678.  
  679.                 /*
  680.                  * get values for size from reference
  681.                  */
  682.             }
  683.             else if (tag->uri_size && get_vartext(tag->uri_size))
  684.                 get_attr_size(hp, tag);
  685.         }
  686.  
  687.         /* call handle if available */
  688.         if (hnd && !hp->fatal)
  689.             hnd_result = (*hnd) (hp, tag);
  690.  
  691.         /* write whole tag out */
  692.         if (write_tag && hnd_result)
  693.         {
  694.             VOID(*tag_callback) (HSCPRC * hp, HSCTAG * tag,
  695.                  STRPTR tag_name, STRPTR tag_attr, STRPTR tag_close) = NULL;
  696.  
  697.             if (open_tag)
  698.             {
  699.                 tag_callback = hp->CB_start_tag;
  700.             }
  701.             else
  702.             {
  703.                 tag_callback = hp->CB_end_tag;
  704.             }
  705.  
  706. #if 1                           /* TODO: remove conditionals, but leave below code intact */
  707.             /* enable output if necessary */
  708.             if (hp->suppress_output)
  709.             {
  710.                 hp_enable_output(hp, "non-internal tag occured");
  711.             }
  712. #endif
  713.  
  714.             /* write (flush) white spaces */
  715.             hsc_output_text(hp, "", "");
  716.  
  717.             if (tag_callback)
  718.             {
  719.                 (*tag_callback) (hp, tag,
  720.                                  estr2str(hp->tag_name_str),
  721.                                  estr2str(hp->tag_attr_str),
  722.                                  estr2str(hp->tag_close_str));
  723.             }
  724.         }
  725.  
  726.         /* skip LF if requested */
  727.         if (tag && (tag->option & HT_SKIPLF))
  728.         {
  729.             skip_next_lf(hp);   /* TODO: really skip single lf */
  730.         }
  731.  
  732.         /* if tag should check for succeeding white spaces,
  733.          * tell this hscprc now */
  734.         if (now_tag_strip_whtspc)
  735.         {
  736.             D(fprintf(stderr, "  requested to check for succ.whtspc\n"));
  737.             hp->tag_next_whtspc = now_tag_strip_whtspc;
  738.         }
  739.  
  740.         /* remove temporary created tag */
  741.         if (unknown_tag)
  742.         {
  743.             del_hsctag(tag);
  744.         }
  745.  
  746. #if (defined MSDOS)             /* HSC_TRIGGER */
  747. #define UNLIKELY (10*1024)
  748.         /* crash randomly */
  749.         if ((rand() % UNLIKELY) == (UNLIKELY / 2))
  750.         {
  751.             enforcerHit();
  752.         }
  753. #endif
  754.     }
  755.  
  756.     return (BOOL) (!hp->fatal);
  757. }
  758.  
  759. /*
  760.  *---------------------------
  761.  * other parse functions
  762.  *---------------------------
  763.  */
  764.  
  765. /* replace icon-entity by image */
  766. static VOID replace_icon(HSCPRC * hp, STRPTR icon)
  767. {
  768.     INFILEPOS *base = new_infilepos(hp->inpf);
  769.     EXPSTR *image = init_estr(0);
  770.     STRPTR s = estr2str(hp->iconbase);
  771.  
  772.     /* create string like <IMG SRC=":icons/back.gif" ALT="back"> */
  773.     set_estr(image, "<IMG SRC=\"");
  774.  
  775.     /* use iconbase with "*" replaced  by iconname as uri */
  776.     while (s[0])
  777.     {
  778.         if (s[0] == '*')
  779.             app_estr(image, icon);
  780.         else
  781.             app_estrch(image, s[0]);
  782.         s++;
  783.     }
  784.  
  785.     /* set ALT attribute to iconname */
  786.     app_estr(image, "\" ALT=\"");
  787.     app_estr(image, icon);
  788.     app_estr(image, "\">");
  789.  
  790.     hsc_message(hp, MSG_RPLC_ICON, "replacing icon-%e", icon);
  791.  
  792.     hsc_include_string(hp, SPECIAL_FILE_ID "include icon",
  793.                        estr2str(image),
  794.                        IH_PARSE_HSC | IH_NO_STATUS | IH_POS_PARENT);
  795.     del_estr(image);
  796.     del_infilepos(base);
  797. }
  798.  
  799. /*
  800.  * hsc_parse_amp
  801.  *
  802.  * parse ampersand ("&")
  803.  */
  804. BOOL hsc_parse_amp(HSCPRC * hp)
  805. {
  806.     INFILE *inpf = hp->inpf;
  807.     EXPSTR *amp_str = init_estr(0);
  808.  
  809.     if (!hp->fatal)
  810.     {
  811.         BOOL rplc = hp->smart_ent;      /* TRUE, if "&" should be replaced */
  812.  
  813.         if (rplc)
  814.         {
  815.             /*
  816.              * test if char before and
  817.              * after "&" is white-space
  818.              */
  819.             int ch = infgetc(inpf);
  820.  
  821.             inungetc(ch, inpf);
  822.  
  823.             if (!(hsc_whtspc(ch)) || !(estrlen(hp->whtspc)))
  824.             {
  825.                 /* no, it is not */
  826.                 rplc = FALSE;
  827.             }
  828.         }
  829.  
  830.         if (rplc)
  831.         {
  832.             /* replace ampersand */
  833.             message_rplc(hp, "&", "&");
  834.             set_estr(amp_str, "&");
  835.         }
  836.         else
  837.         {
  838.             /*
  839.              * get entity-id, test for unknown entity
  840.              */
  841.             char *nxtwd;
  842.             DLNODE *nd;
  843.             BOOL app_entity = TRUE;
  844.  
  845.             /* remember "&" */
  846.             set_estr(amp_str, infgetcw(inpf));
  847.  
  848.             /* get entity id */
  849.             nxtwd = infgetw(inpf);
  850.  
  851.             /* TODO: check for white-space */
  852.  
  853.             if (!strcmp(nxtwd, "\\"))
  854.             {
  855.                 /* flush white spaces */
  856.                 clr_estr(hp->whtspc);
  857.                 clr_estr(amp_str);
  858.                 infskip_ws(inpf);
  859.             }
  860.             else if (!strcmp(nxtwd, "/"))
  861.             {
  862.                 /* replace white spaces by a single blank */
  863.                 set_estr(hp->whtspc, " ");
  864.                 clr_estr(amp_str);
  865.                 infskip_ws(inpf);
  866.             }
  867.             else
  868.             {
  869.                 hp_enable_output(hp, "entity");
  870.  
  871.                 if (!strcmp(nxtwd, "#"))
  872.                 {
  873.                     /*
  874.                      * process numeric entity
  875.                      */
  876.  
  877.                     /* append "#" */
  878.                     app_estr(amp_str, infgetcw(inpf));
  879.  
  880.                     nxtwd = infgetw(inpf);
  881.                     errno = 0;
  882.                     strtoul(nxtwd, NULL, 0);
  883.                     if (errno || strlen(infgetcws(inpf)))
  884.                     {
  885.                         hsc_message(hp, MSG_ILLG_NUM,   /* illegal numeric entity */
  886.                               "illegal numeric value %n for entity", nxtwd);
  887.                     }
  888.                     /* append entity specifier */
  889.                     app_estr(amp_str, nxtwd);
  890.                 }
  891.                 else
  892.                 {
  893.                     /*
  894.                      * process text entity
  895.                      */
  896.                     HSCVAR *attr = NULL;
  897.  
  898.                     /* search for entity in list */
  899.                     nd = find_dlnode(hp->defent->first, (APTR) nxtwd, cmp_strent);
  900.  
  901.                     if (hp->jens && (nd == NULL))
  902.                     {
  903.                         /* asume that entity is an attribute,
  904.                          * try to find it and append it's value
  905.                          */
  906.                         attr = find_varname(hp->defattr, nxtwd);
  907.                         if (attr)
  908.                         {
  909.                             set_estr(amp_str, get_vartext(attr));
  910.                             app_entity = FALSE;
  911.                         }
  912.                     }
  913.  
  914.                     if ((nd == NULL) && (attr == NULL))
  915.                     {
  916.                         hsc_message(hp, MSG_UNKN_ENTITY,
  917.                                     "unknown %e", nxtwd);
  918.                     }
  919.                     else
  920.                     {
  921.                         /* check for icon-entity and warn about */
  922.                         /* portability problem */
  923.                         HSCENT *entity = dln_data(nd);
  924.  
  925.                         if (entity->numeric == ICON_ENTITY)
  926.                             if (estrlen(hp->iconbase))
  927.                             {
  928.                                 replace_icon(hp, nxtwd);
  929.                                 set_estr(amp_str, "");
  930.                                 app_entity = FALSE;
  931.                             }
  932.                             else
  933.                             {
  934.                                 hsc_message(hp, MSG_ICON_ENTITY,
  935.                                             "icon %e found", nxtwd);
  936.                             }
  937.                     }
  938.  
  939.                     if (app_entity)
  940.                     {
  941.                         /* append entity specifier */
  942.                         app_estr(amp_str, nxtwd);
  943.                     }
  944.                 }
  945.  
  946.                 /* check for closing ';' */
  947.                 nxtwd = infgetw(inpf);
  948.                 if (nxtwd)
  949.                 {
  950.                     if (!strcmp(nxtwd, ";"))
  951.                     {
  952.                         if (strlen(infgetcws(inpf)))
  953.                         {
  954.                             hsc_msg_illg_whtspc(hp);
  955.                         }
  956.  
  957.                         if (app_entity)
  958.                         {
  959.                             app_estr(amp_str, infgetcws(inpf));
  960.                             app_estr(amp_str, infgetcw(inpf));
  961.                         }
  962.                     }
  963.                     else
  964.                     {
  965.                         hsc_message(hp, MSG_EXPT_SEMIC, "%q expected after entity", ";");
  966.                         inungetcwws(inpf);
  967.                     }
  968.                 }
  969.                 else
  970.                 {
  971.                     hsc_msg_eof(hp, "expected \";\") for entity");
  972.                 }
  973.             }
  974.         }
  975.  
  976.         /* output whole entity */
  977.         if (estrlen(amp_str))
  978.         {
  979.             hsc_output_text(hp, "", estr2str(amp_str));
  980.         }
  981.  
  982.         del_estr(amp_str);
  983.  
  984. #if (defined MSDOS)             /* HSC_BILL */
  985. #define WASTE_SIZE (1024*1024)
  986.         /* waste some time */
  987.         {
  988.             STRPTR mem1 = (STRPTR) umalloc(WASTE_SIZE);
  989.             STRPTR mem2 = (STRPTR) umalloc(WASTE_SIZE);
  990.             size_t i = WASTE_SIZE;
  991.  
  992.             while (i)
  993.             {
  994.                 if (mem1[i] && mem2[i])
  995.                 {
  996.                     mem1[i] = mem2[i];
  997.                 }
  998.                 else
  999.                 {
  1000.                     mem2[i] = mem1[i];
  1001.                 }
  1002.                 i--;
  1003.             }
  1004.  
  1005.             ufree(mem2);
  1006.             ufree(mem1);
  1007.         }
  1008. #endif
  1009.     }
  1010.  
  1011.     return (BOOL) (!hp->fatal);
  1012. }
  1013.  
  1014. /*
  1015.  * hsc_parse_text
  1016.  */
  1017. BOOL hsc_parse_text(HSCPRC * hp)
  1018. {
  1019.     INFILE *inpf = hp->inpf;
  1020.     STRPTR nw = infgetcw(inpf);
  1021.  
  1022.     if (nw && hp->suppress_output)
  1023.     {
  1024.         hp_enable_output(hp, "some text");
  1025.     }
  1026.  
  1027.     if (nw)
  1028.     {                           /* do test below only if not end-of-file */
  1029.         /*
  1030.          * check unmatched ">"
  1031.          */
  1032.         if (!strcmp(nw, ">"))
  1033.         {
  1034.             BOOL rplc = hp->smart_ent;  /* TRUE, if ">" should be replaced */
  1035.  
  1036.             if (rplc)
  1037.             {
  1038.                 /*
  1039.                  * test if char before and
  1040.                  * after ">" is white-space
  1041.                  */
  1042.                 int ch = infgetc(inpf);
  1043.  
  1044.                 inungetc(ch, inpf);
  1045.  
  1046.                 if (!(hsc_whtspc(ch) && estrlen(hp->whtspc)))
  1047.                 {
  1048.                     rplc = FALSE;
  1049.                 }
  1050.             }
  1051.             if (rplc)
  1052.             {
  1053.                 /* replace gt */
  1054.                 message_rplc(hp, nw, ">");
  1055.                 nw = ">";
  1056.             }
  1057.             else
  1058.             {
  1059.                 hsc_message(hp, MSG_UNMA_GT, "unmatched %q", ">");
  1060.             }
  1061.         }
  1062.         /*
  1063.          * check for quote
  1064.          */
  1065.         else if (!strcmp(nw, "\""))
  1066.         {
  1067.             if (hp->rplc_quote)
  1068.             {
  1069.                 /* replace quote */
  1070.                 message_rplc(hp, nw, """);
  1071.                 nw = """;
  1072.             }
  1073.         }
  1074.         /*
  1075.          * check for entities to replace
  1076.          */
  1077.         else
  1078.         {
  1079.             DLNODE *nd = NULL;  /* entity search result */
  1080.  
  1081.             if (hp->rplc_ent && (strlen(nw) == 1)
  1082.                 && (((UBYTE) nw[0]) >= 127))
  1083.             {
  1084.                 nd = find_dlnode(hp->defent->first, (APTR) nw, cmp_rplcent);
  1085.  
  1086.                 if (nd)
  1087.                 {
  1088.                     BOOL ok = TRUE;
  1089.  
  1090.                     /* copy replaced entity to buffer */
  1091.                     ok &= set_estr(hp->tmpstr, "&");
  1092.                     ok &= app_estr(hp->tmpstr, ((HSCENT *) nd->data)->name);
  1093.                     ok &= app_estr(hp->tmpstr, ";");
  1094.  
  1095.                     if (ok)
  1096.                     {
  1097.                         /* replace-message */
  1098.                         message_rplc(hp, nw, estr2str(hp->tmpstr));
  1099.                         nw = estr2str(hp->tmpstr);
  1100.                     }
  1101.                 }
  1102.             }
  1103.             /*
  1104.              * check for "click here" syndrome
  1105.              */
  1106.             if (hp->inside_anchor && hp->click_here_str)
  1107.             {
  1108.                 ULONG found = strenum(nw, hp->click_here_str, '|', STEN_NOCASE);
  1109.                 if (found)
  1110.                 {
  1111.                     hsc_message(hp, MSG_CLICK_HERE,
  1112.                                 "%q-syndrome detected", "click here");
  1113.                 }
  1114.             }
  1115.  
  1116. #if (defined MSDOS)             /* HSC_PLEASE */
  1117.             /* replace certain keywords */
  1118.             if (!upstrcmp(nw, "Netscape"))
  1119.             {
  1120.                 nw = "Nutscape";
  1121.             }
  1122.             else if (!upstrcmp(nw, "Microsoft"))
  1123.             {
  1124.                 nw = "Mircosoft";
  1125.             }
  1126.             else if (!upstrcmp(nw, "Intel"))
  1127.             {
  1128.                 nw = "Wintel";
  1129.             }
  1130.             /* to be continued.. */
  1131. #endif
  1132.         }
  1133.     }
  1134.  
  1135.     if (nw)
  1136.         hsc_output_text(hp, "", nw);    /* output word */
  1137.  
  1138.     return (BOOL) (!hp->fatal);
  1139. }
  1140.  
  1141. /*
  1142.  * hsc_parse
  1143.  *
  1144.  * parse input chars with full hsc support
  1145.  *
  1146.  * params: inpf...input file
  1147.  *
  1148.  * result: TRUE, if no error
  1149.  */
  1150. BOOL hsc_parse(HSCPRC * hp)
  1151. {
  1152.     if (!hp->fatal)
  1153.     {
  1154.         STRPTR nxtwd = infgetw(hp->inpf);
  1155.         STRPTR cws = infgetcws(hp->inpf);       /* current WhtSpcs */
  1156.  
  1157.         /* add white spaces to buffer */
  1158.         if (cws)
  1159.         {
  1160.             app_estr(hp->whtspc, cws);
  1161.         }
  1162.  
  1163.         /* parse text */
  1164.         if (nxtwd)
  1165.         {
  1166.             if (!strcmp(nxtwd, "<"))
  1167.             {
  1168.                 /* parse tag */
  1169.                 hsc_parse_tag(hp);
  1170.             }
  1171.             else if (!strcmp(nxtwd, "&"))
  1172.             {
  1173.                 /* parse entity */
  1174.                 hsc_parse_amp(hp);
  1175.             }
  1176.             else
  1177.             {
  1178.                 /* handle text */
  1179.                 hsc_parse_text(hp);
  1180.             }
  1181.         }
  1182.         else
  1183.         {
  1184. #if 0                           /* TODO: remove, this is now done in hsc_parse_end() */
  1185.             /* output last white spaces at eof */
  1186.             hsc_output_text(hp, "", "");
  1187. #endif
  1188.         }
  1189.     }
  1190.  
  1191.     return (BOOL) (!hp->fatal);
  1192. }
  1193.  
  1194. /*
  1195.  * hsc_parse_source
  1196.  *
  1197.  * parse input chars with full hsc support
  1198.  *
  1199.  * params: inpf...input file
  1200.  *
  1201.  * result: TRUE, if no error
  1202.  */
  1203. BOOL hsc_parse_source(HSCPRC * hp)
  1204. {
  1205.     if (!hp->fatal)
  1206.     {
  1207.         STRPTR nxtwd = infgetw(hp->inpf);
  1208.         STRPTR cws = infgetcws(hp->inpf);       /* current WhtSpcs */
  1209.  
  1210.         /* add white spaces to buffer */
  1211.         if (cws)
  1212.         {
  1213.             app_estr(hp->whtspc, cws);
  1214.         }
  1215.  
  1216.         if (nxtwd)
  1217.         {
  1218.             /* process next word */
  1219.             if (!strcmp(nxtwd, "<"))
  1220.             {
  1221.                 hsc_output_text(hp, "", "<");
  1222.             }
  1223.             else if (!strcmp(nxtwd, ">"))
  1224.             {
  1225.                 hsc_output_text(hp, "", ">");
  1226.             }
  1227.             else if (!strcmp(nxtwd, "&"))
  1228.             {
  1229.                 hsc_output_text(hp, "", "&");
  1230.             }
  1231.             else
  1232.             {
  1233.                 hsc_parse_text(hp);
  1234.             }
  1235.         }
  1236.     }
  1237.     return (BOOL) (!hp->fatal);
  1238. }
  1239.  
  1240. /*
  1241.  *---------------------------
  1242.  * parse end functions
  1243.  *---------------------------
  1244.  */
  1245.  
  1246. /*
  1247.  * hsc_parse_end
  1248.  *
  1249.  * check for all tags closed and required
  1250.  * tags occured
  1251.  */
  1252. BOOL hsc_parse_end(HSCPRC * hp)
  1253. {
  1254.     if (!hp->fatal)
  1255.     {
  1256.         /* remember current file position */
  1257.         INFILEPOS *infpos = new_infilepos(hp->inpf);
  1258.         DLNODE *nd = NULL;
  1259.  
  1260.         /* check for unclosed containers:
  1261.          * for every container tag still on stack launch a message,
  1262.          * exept for autoclose tags */
  1263.         nd = hp->container_stack->last;
  1264.         while (nd)
  1265.         {
  1266.             HSCTAG *endtag = (HSCTAG *) dln_data(nd);
  1267.  
  1268.             if (!(endtag->option & HT_AUTOCLOSE))
  1269.             {
  1270.                 set_infilepos(hp->inpf, endtag->start_fpos);
  1271.                 hsc_message(hp, MSG_MISS_CTAG,
  1272.                             "%c missing", endtag->name);
  1273.             }
  1274.  
  1275.             nd = dln_prev(nd);
  1276.         }
  1277.  
  1278.         /* restore file position */
  1279.         set_infilepos(hp->inpf, infpos);
  1280.         del_infilepos(infpos);
  1281.  
  1282.         /* check for required and recommended tags missing */
  1283.         nd = dll_first(hp->deftag);
  1284.         while (nd)
  1285.         {
  1286.             HSCTAG *tag = (HSCTAG *) dln_data(nd);
  1287.  
  1288.             if ((tag->option & HT_REQUIRED
  1289.                  && (tag->occured == FALSE)))
  1290.             {
  1291.                 hsc_message(hp, MSG_MISS_REQTAG,
  1292.                             "required %T missing", tag);
  1293.             }
  1294.             else if ((tag->option & HT_RECOMMENDED
  1295.                       && (tag->occured == FALSE)))
  1296.             {
  1297.                 hsc_message(hp, MSG_MISS_RCMDTAG,
  1298.                             "recommended %T missing", tag);
  1299.             }
  1300.             nd = dln_next(nd);
  1301.         }
  1302.  
  1303.         /* output last white spaces at eof */
  1304.         hsc_output_text(hp, "", "");
  1305.     }
  1306.     return (BOOL) (!hp->fatal);
  1307. }
  1308.  
  1309. /*
  1310.  *---------------------------
  1311.  * parse IDs functions
  1312.  *---------------------------
  1313.  */
  1314.  
  1315. /*
  1316.  * hsc_parse_end_id
  1317.  *
  1318.  * append all locally defined IDs to global IDs,
  1319.  * check all before referenced local IDs
  1320.  *
  1321.  */
  1322. BOOL hsc_parse_end_id(HSCPRC * hp)
  1323. {
  1324.     if (!hp->fatal)
  1325.         check_all_local_idref(hp);      /* check local IDs */
  1326.  
  1327.     return (BOOL) (!hp->fatal);
  1328. }
  1329.